/*
 * @Author: 13177709051 3506463075@qq.com
 * @Date: 2024-10-23 20:32:52
 * @LastEditors: 13177709051 3506463075@qq.com
 * @LastEditTime: 2024-10-23 22:13:38
 * @FilePath: /linux22/shm/Shm.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#pragma

#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
 #include <string.h>

const int gCreater = 1;
const int gUser = 2;
const std::string gpathname = "/home/cjb/linux-operating-system-network/linux22/shm";
const int gproj_id = 0x66;
const int gShmSize = 4097; // 4096*n

class Shm
{
private:
    key_t GetCommKey()
    {
        key_t k = ftok(_pathname.c_str(), _proj_id);
        if (k < 0)
        {
            perror("ftok");
        }
        return k;
    }
    int GetShmHelper(key_t key, int size, int flag)
    {
        int shmid = shmget(key, size, flag);
        if (shmid < 0)
        {
            perror("shmget");
        }
        return shmid;
    }

    std::string RoleToString(int who)
    {
        if (who == gCreater)
            return "Creater";
        else if (who == gUser)
            return "gUser";
        else
            return "None";
    }
    void *AttachShm()
    {
        if (_addrshm != nullptr)
            DetachShm(_addrshm);
        void *shmaddr = shmat(_shmid, nullptr, 0);
        if (shmaddr == nullptr)
        {
            perror("shmat");
        }
        std::cout << "who: " << RoleToString(_who) << "attach shm..." << std::endl;
        return shmaddr;
    }

    void DetachShm(void *shmadder)
    {
        if (shmadder == nullptr)
            return;
        shmdt(shmadder);
        std::cout << "who: " << RoleToString(_who) << "detach shm..." << std::endl;
    }

public:
    Shm(const std::string &pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr)
    {
        _key = GetCommKey();
        if (_who == gCreater)
            GetShmUseCreate();
        else if (_who == gUser)
            GetShmForUse();
        _addrshm = AttachShm();

        std::cout << "shmid: " << _shmid << std::endl;
        std::cout << "_key: " << ToHex(_key) << std::endl;
    }

    ~Shm()
    {
        if (_who == gCreater)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr);
        }
        std::cout << "shm remove done..." << std::endl;
    }

    std::string ToHex(key_t key)
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }

    bool GetShmUseCreate()
    {
        if (_who == gCreater)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
            if (_shmid >= 0)
                return true;
            std::cout << "shm create done..." << std::endl;
        }
        return false;
    }

    bool GetShmForUse()
    {
        if (_who == gUser)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
            if (_shmid >= 0)
                return true;
            std::cout << "shm get done..." << std::endl;
        }
        return false;
    }

    void Zero()
    {
        if (_addrshm)
        {
            memset(_addrshm, 0, gShmSize);
        }
    }

    void *Addr()
    {
        return _addrshm;
    }

    void DebugShm()
    {
        struct shmid_ds ds;
        int n = shmctl(_shmid, IPC_STAT, &ds);
        if (n < 0)
            return;
        std::cout << "ds.shm_prem.__key : " << ToHex(ds.shm_perm.__key) << std::endl;
        std::cout << "ds.shm_nattch" << ds.shm_nattch << std::endl;
    }

private:
    key_t _key;
    int _shmid;

    std::string _pathname;
    int _proj_id;

    int _who;
    void *_addrshm;
};




// 1. 常量定义
// gCreater 和 gUser：分别表示创建者和用户角色的常量，用于区分不同的操作角色。
// gpathname：共享内存文件路径，ftok 使用此路径生成唯一的键。
// gproj_id：用于生成唯一键的项目 ID。
// gShmSize：共享内存的大小，设置为 4097 字节（必须是 4096 的倍数）。
// 2. 私有方法
// GetCommKey()：

// 使用 ftok 函数生成共享内存的键。如果生成失败，打印错误信息。
// GetShmHelper()：

// 封装对 shmget 的调用，尝试获取共享内存 ID。如果失败，打印错误信息。
// RoleToString()：

// 将角色 ID 转换为字符串，方便调试输出。
// AttachShm()：

// 尝试附加共享内存。如果 _addrshm 不为空，先调用 DetachShm() 分离它。使用 shmat 函数附加共享内存，并打印调试信息。
// DetachShm()：

// 封装对 shmdt 的调用，用于分离共享内存。如果传入的指针为空，直接返回。
// 3. 公共方法
// 构造函数：

// 接受路径、项目 ID 和角色，并初始化相关变量。
// 调用 GetCommKey() 获取键。
// 根据角色调用 GetShmUseCreate() 或 GetShmForUse() 来获取共享内存。
// 调用 AttachShm() 附加共享内存，打印共享内存 ID 和键。
// 析构函数：

// 如果角色是创建者，调用 shmctl 删除共享内存（IPC_RMID）。
// ToHex()：

// 将共享内存键转换为十六进制字符串，方便输出调试信息。
// GetShmUseCreate()：

// 仅在创建者角色时调用，尝试创建共享内存段（IPC_CREAT | IPC_EXCL），成功返回 true。
// GetShmForUse()：

// 仅在用户角色时调用，尝试获取共享内存段（IPC_CREAT），成功返回 true。
// Zero()：

// 清零共享内存内容，使用 memset。
// Addr()：

// 返回当前附加的共享内存地址。
// DebugShm()：

// 获取共享内存的状态信息（如键和连接数），使用 shmctl 和 IPC_STAT。
// 4. 成员变量
// _key：存储生成的共享内存键。
// _shmid：共享内存 ID。
// _pathname：存储共享内存路径。
// _proj_id：存储项目 ID。
// _who：当前对象的角色（创建者或用户）。
// _addrshm：指向当前附加的共享内存的指针。